import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import form.CodeType;
import form.MvvmCreateDialog;
import form.Utils;
import org.apache.http.util.TextUtils;
import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;

import static form.CodeType.*;

/**
 * Mvvm
 */
public class MvvmCreateAction extends AnAction {
    private static final String SRC_MAIN = "src/main/"; // 文件目录结构
    private String mModelName; // 需要创建的Mvvm名字
    private CodeType mCodeType; // 需要创建的Mvvm类型
    private boolean isCreateModel; // 是否需要创建Model
    private boolean isCreateKotlin; // 是否为创建Kotlin文件
    private Project project; // 项目
    // 需要替换的新的Base包名
    private String needNewBasePackageName = "";
    private VirtualFile currentFile; // 当前选择文件
    private String currentFileAbsolutePath; // 当前选择文件绝对路径
    private String currentFilePackageName; // 当前选择文件包名
    private String resLayoutAbsolutePath; // 布局资源文件绝对路径
    private String basePackageName; // 当前module包名

    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        project = e.getData(PlatformDataKeys.PROJECT);
        currentFile = e.getData(PlatformDataKeys.VIRTUAL_FILE);
        if (isExistsUnderSrc()) {
            currentFileAbsolutePath = getCurrentFileAbsolutePath();
            currentFilePackageName = getCurrentFilePackageName();
            String packageNamePath = currentFilePackageName.replace(".", "/");
            if (!currentFileAbsolutePath.contains(packageNamePath)) {
                currentFileAbsolutePath = currentFileAbsolutePath + "/" + packageNamePath;
            }
            resLayoutAbsolutePath = getResLayoutAbsolutePath();
            basePackageName = getBasePackageName();
            findPackageName();
            // 初始化项目Dialog
            initDialog();
            refreshProject(e);
        }
    }

    /**
     * 刷新项目
     */
    private void refreshProject(@NotNull AnActionEvent e) {
        e.getProject().getBaseDir().refresh(false, true);
    }

    /**
     * 初始化项目Dialog
     */
    private void initDialog() {
        MvvmCreateDialog dialog = new MvvmCreateDialog((className, codeType, isCreateModel, isCreateKotlin) -> {
            mModelName = className;
            mCodeType = codeType;
            MvvmCreateAction.this.isCreateModel = isCreateModel;
            MvvmCreateAction.this.isCreateKotlin = isCreateKotlin;
            createClassFiles();
            Messages.showInfoMessage(project, "create mvvm code success", "Success");
        });
        dialog.pack();
        dialog.setVisible(true);
    }

    /**
     * 找新的Base包名
     */
    private void findPackageName() {
        int relativeLength = currentFileAbsolutePath.length() - currentFilePackageName.length();
        // 相对路径
        String relativePath = currentFileAbsolutePath.substring(0, relativeLength);
        //
        String packageName = Utils.getMvvmBasePackagePath();
        // 判断该文件是否存在
        boolean exists = isFileExists(relativePath + packageName.replace(".", "/"));
        if (exists) {
            needNewBasePackageName = packageName;
            return;
        }
        //
        String packageNameMore = Utils.getMvvmBasePackagePathMore();
        String[] split = packageNameMore.split(";");
        for (String name : split) {
            if (!TextUtils.isEmpty(name)) {
                boolean isExists = isFileExists(relativePath + name.replace(".", "/"));
                if (isExists) {
                    needNewBasePackageName = name;
                    return;
                }
            }
        }
        if (TextUtils.isEmpty(needNewBasePackageName)) {
            needNewBasePackageName = packageName;
        }
    }

    /**
     * 判断文件是否存在
     */
    private boolean isFileExists(String classPath) {
        try {
            File floder = new File(classPath);
            return floder.exists();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 生成类文件
     */
    private void createClassFiles() {
        // if (null == mCodeType) return;
        switch (mCodeType) {
            case Activity:
                createClassFile(Activity);
                break;
            case Fragment:
                createClassFile(Fragment);
                break;
        }
        createClassFile(ViewModel);
        if (isCreateModel) {
            createClassFile(Model);
        }
        createClassFile(XML);
        if (mCodeType == Activity) {
            createClassFile(AndroidManifest);
        }
    }

    /**
     * 生成mvvm框架代码
     */
    private void createClassFile(@NotNull CodeType codeType) {
        String fileName = "";
        String content = "";
        String appPath = currentFileAbsolutePath;
        String resPath = resLayoutAbsolutePath;
        switch (codeType) {
            case Activity:
                if (isCreateKotlin) {
                    fileName = "TemplateActivity.txt";
                } else {
                    fileName = "TemplateActivityJava.txt";
                }
                content = readTemplateFile(fileName);
                content = dealTemplateContent(content);
                writeToFile(content, appPath, mModelName + "Activity." + (isCreateKotlin ? "kt" : "java"));
                break;
            case Fragment:
                if (isCreateKotlin) {
                    fileName = "TemplateFragment.txt";
                } else {
                    fileName = "TemplateFragmentJava.txt";
                }
                content = readTemplateFile(fileName);
                content = dealTemplateContent(content);

                writeToFile(content, appPath, mModelName + "Fragment." + (isCreateKotlin ? "kt" : "java"));
                break;
            case ViewModel:
                if (isCreateKotlin) {
                    fileName = "TemplateViewModel.txt";
                } else {
                    fileName = "TemplateViewModelJava.txt";
                }
                content = readTemplateFile(fileName);
                content = dealTemplateContent(content);
                writeToFile(content, appPath, mModelName + "ViewModel." + (isCreateKotlin ? "kt" : "java"));
                break;
            case Model:
                if (isCreateKotlin) {
                    fileName = "TemplateModel.txt";
                } else {
                    fileName = "TemplateModelJava.txt";
                }
                content = readTemplateFile(fileName);
                content = dealTemplateContent(content);
                writeToFile(content, appPath, mModelName + "Model." + (isCreateKotlin ? "kt" : "java"));
                break;
            case XML:
                fileName = "TemplateXML.txt";
                content = readTemplateFile(fileName);
                content = dealTemplateContent(content);
                // 前缀
                String prefix = "activity";
                if (mCodeType == Fragment) {
                    prefix = "fragment";
                }
                writeToFile(content, resPath, prefix + "_" + theHumpIsCrossed(mModelName) + ".xml");
                break;
            case AndroidManifest:
                content = readAndroidManifestFile();
                if (!TextUtils.isEmpty(content)) {
                    content = dealTemplateContent(content);
                    String path = getAndroidManifestAbsolutePath();
                    writeToFile(content, path.substring(0, path.indexOf("AndroidManifest.xml") - 1), "AndroidManifest.xml");
                }
                break;
        }
    }

    /**
     * 生成
     *
     * @param content   类中的内容
     * @param classPath 类文件路径
     * @param className 类文件名称
     */
    private void writeToFile(String content, String classPath, String className) {
        Utils.writeToFile(content, classPath, className);
    }

    /**
     * 驼峰转下划线
     */
    private String theHumpIsCrossed(@NotNull String trim) {
        String[] split = trim.split("(?<!^)(?=[A-Z])");
        int count = null == split ? 0 : split.length;
        trim = "";
        for (int i = 0; i < count; i++) {
            if (TextUtils.isEmpty(split[i])) {
                continue;
            }
            trim = trim + "_" + split[i].toLowerCase();
        }
        trim = trim.substring(1);
        return trim;
    }

    /**
     * 替换模板中字符
     */
    private String dealTemplateContent(String content) {
        // 替换当前类名
        content = content.replace("$CLASS_NAME$", mModelName);
        // 替换资源文件导入
        if (content.contains("$CLASS_NAME_LOWER_CASE$")) {
            content = content.replace("$CLASS_NAME_LOWER_CASE$", theHumpIsCrossed(mModelName));
        }
        // 替换Model Name
        if (content.contains("$CLASS_MODEL_NAME$")) {
            if (isCreateModel) {
                content = content.replace("$CLASS_MODEL_NAME$", mModelName);
            } else {
                content = content.replace("$CLASS_MODEL_NAME$", "Base");
            }
        }
        // 替换当前包名
        if (content.contains("$PACKAGE_NAME$")) {
            content = content.replace("$PACKAGE_NAME$", currentFilePackageName);
        }
        // 替换R包名
        if (content.contains("$BASE_PACKAGE_NAME$")) {
            content = content.replace("$BASE_PACKAGE_NAME$", basePackageName);
        }
        // 替换实例化Model
        if (content.contains("$MODEL_NAME$")) {
            if (isCreateModel) {
                if (isCreateKotlin) {
                    content = content.replace("$MODEL_NAME$", mModelName + "Model()");
                } else {
                    content = content.replace("$MODEL_NAME$", "new " + mModelName + "Model()");
                }
            } else {
                content = content.replace("$MODEL_NAME$", "null");
            }
        }
        // 替换Base包名
        if (content.contains("$NEED_NEW_BASE_PACKAGE_NAME$")) {
            content = content.replace("$NEED_NEW_BASE_PACKAGE_NAME$", needNewBasePackageName);
        }
        // 替换 AndroidManifest 文件内容
        if (content.contains("</application>")) {
            String mReplaceContent = String.format(REPLACE_CONTENT, currentFilePackageName, mModelName);
            content = content.replace("</application>", mReplaceContent);
        }
        return content;
    }

    // AndroidManifest 文件需要替换的内容
    private static final String REPLACE_CONTENT = "    <activity\n" +
            "            android:name=\"%s.%sActivity\"\n" +
            "            android:screenOrientation=\"portrait\" />\n" +
            "\n" +
            "    </application>";

    /**
     * 读取AndroidManifest内容
     */
    private String readAndroidManifestFile() {
        // final String path = "...\\app\\src\\main\\AndroidManifest.xml";
        File file = new File(getAndroidManifestAbsolutePath());
        if (!file.exists()) return "";
        String content = "";
        try {
            FileInputStream inputStream = new FileInputStream(file);
            content = new String(readStream(inputStream));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return content;
    }

    /**
     * 读取模板文件中的字符内容
     *
     * @param fileName 模板文件名
     */
    private String readTemplateFile(String fileName) {
        InputStream in = null;
        // ..\MvvmCreatePlugin\src\template\TemplateActivity.txt
        in = this.getClass().getResourceAsStream("/template/" + fileName);
        String content = "";
        try {
            content = new String(readStream(in));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return content;
    }

    private byte[] readStream(@NotNull InputStream inputStream) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = -1;
        try {
            while ((len = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            outputStream.close();
            inputStream.close();
        }
        return outputStream.toByteArray();
    }

    /**
     * 获取当前选择文件的包名
     */
    private String getCurrentFilePackageName() {
        String absolutePath = getCurrentFileAbsolutePath();
        int startIndex = absolutePath.indexOf(SRC_MAIN);
        String relativePath = absolutePath.substring(startIndex);
        String[] split = relativePath.split("/");
        int length = split.length;
        if (length > 3) {
            // 包名
            StringBuilder packageName = new StringBuilder();
            for (int i = 3; i < length; i++) {
                packageName.append(split[i]);
                if (i != length - 1) {
                    packageName.append(".");
                }
            }
            return packageName.toString();
        }
        return getBasePackageName();
    }

    /**
     * 从AndroidManifest.xml文件中获取当前app的包名
     */
    private String getBasePackageName() {
        String package_name = "";
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        try {
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document doc = db.parse(getAndroidManifestAbsolutePath());

            NodeList nodeList = doc.getElementsByTagName("manifest");
            for (int i = 0; i < nodeList.getLength(); i++) {
                Node node = nodeList.item(i);
                Element element = (Element) node;
                package_name = element.getAttribute("package");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return package_name;
    }

    /**
     * 判断当前选中文件是否存在于Src下
     */
    private boolean isExistsUnderSrc() {
        return getCurrentFileAbsolutePath().contains(SRC_MAIN);
    }

    /**
     * 获取当前module的资源布局文件绝对路径
     */
    @NotNull
    private String getResLayoutAbsolutePath() {
        String absolutePath = getCurrentFileAbsolutePath();
        return absolutePath.substring(0, absolutePath.indexOf(SRC_MAIN)) + SRC_MAIN + "res/layout";
    }

    /**
     * 获取当前module AndroidManifest绝对路径
     */
    @NotNull
    private String getAndroidManifestAbsolutePath() {
        String absolutePath = getCurrentFileAbsolutePath();
        return absolutePath.substring(0, absolutePath.indexOf(SRC_MAIN)) + SRC_MAIN + "AndroidManifest.xml";
    }

    /**
     * 获取当前文件绝对路径
     */
    private String getCurrentFileAbsolutePath() {
        String absolutePath = null == currentFile ? "" : currentFile.getPath();
        if (isFile()) {
            absolutePath = absolutePath.substring(0, absolutePath.lastIndexOf("/"));
        }
        return absolutePath;
    }

    /**
     * 判断当前文件是否为文件，反之则为包名
     */
    private boolean isFile() {
        return getCurrentFileName().contains(".");
    }

    /**
     * 获取当前文件名
     */
    @NotNull
    private String getCurrentFileName() {
        return null == currentFile ? "" : currentFile.getName();
    }
}